#include <QCompleter>
#include <QAbstractItemView>
#include <QScrollBar>
#include "caltextedit.h"

CalTextEdit::CalTextEdit(QWidget *parent): QTextEdit(parent)
{
    this->setFont(QFont("Courier New",10));    
    highlighter = new Highlighter(this->document());

    QTextFrameFormat topFrameFormat = this->document()->rootFrame()->frameFormat();
    topFrameFormat.setPadding(5);
    this->document()->rootFrame()->setFrameFormat(topFrameFormat);

    QTextCursor cursor(this->textCursor());
    cursor.insertText("---> ");

    this->document()->setModified(false);
}
void CalTextEdit::setCompleter(QCompleter *completer)
{
    if (c)
        QObject::disconnect(c, 0, this, 0);

    c = completer;

    if (!c)
        return;

    c->setWidget(this);
    c->setCompletionMode(QCompleter::PopupCompletion);
    c->setCaseSensitivity(Qt::CaseInsensitive);
    QObject::connect(c, SIGNAL(activated(const QString&)),
                     this, SLOT(insertCompletion(const QString&)));
}
QCompleter *CalTextEdit::completer() const
{
    return c;
}
void CalTextEdit::insertCompletion(const QString &completion)
{
    if (c->widget() != this)
        return;
    QTextCursor tc = textCursor();
    int extra = completion.length() - c->completionPrefix().length();
    tc.movePosition(QTextCursor::Left);
    tc.movePosition(QTextCursor::EndOfWord);
    tc.insertText(completion.right(extra));
    setTextCursor(tc);
}
void CalTextEdit::focusInEvent(QFocusEvent *e)
{
    if (c)
        c->setWidget(this);
    QTextEdit::focusInEvent(e);
}

QString CalTextEdit::textUnderCursor() const
{
    QTextCursor tc = textCursor();
    tc.select(QTextCursor::WordUnderCursor);
    return tc.selectedText();
}

int CalTextEdit::showResult(QString result)
{
    //result.trimmed();
    bool isModifier = false;
    if(result.indexOf("error",0) != -1 || result.indexOf("(%i1)",0) != -1 || result.indexOf("http://maxima",0) != -1)
        return 0;
    // reformat result
    int position = result.lastIndexOf("(%i");
    result.remove(position-1,10);
    // show result
    QTextCursor cursor(this->textCursor());
    do{
        if(cursor.movePosition(QTextCursor::NextBlock))
        {
            isModifier = true;
            if(isLineCommand(cursor.block()))
            {
                cursor.movePosition(QTextCursor::PreviousBlock);
                cursor.movePosition(QTextCursor::EndOfBlock);
                break;
            }
            else
            {
                cursor.select(QTextCursor::BlockUnderCursor);
                cursor.removeSelectedText();
            }
        }
        else
        {
            cursor.setPosition(this->document()->rootFrame()->lastPosition());
            break;
        }
    }while(1);
    cursor.insertBlock();
    cursor.insertText(result);
    return isModifier ? false : true;
}
void CalTextEdit::createNewCommand()
{
    QTextCursor cursor(this->textCursor());
    cursor.setPosition(this->document()->rootFrame()->lastPosition());
    cursor.insertBlock();            
    cursor.insertText(tr("---> "));
    this->setTextCursor(cursor);
}

void CalTextEdit::keyPressEvent(QKeyEvent* event)
{
    QTextCursor cursor(this->textCursor());
    if(!isLineCommand(cursor.block())
        && event->key() != Qt::Key_Up
        && event->key() != Qt::Key_Down
        && event->key() != Qt::Key_Left
        && event->key() != Qt::Key_Right)
        return;
    if((cursor.block().position() + 5 > cursor.position()) && isLineCommand(cursor.block()))
        return;

    if (c && c->popup()->isVisible()) {
        // The following keys are forwarded by the completer to the widget
       switch (event->key()) {
       case Qt::Key_Enter:
       case Qt::Key_Return:
       case Qt::Key_Escape:
       case Qt::Key_Tab:
       case Qt::Key_Backtab:
            event->ignore();
            return; // let the completer do default behavior
       default:
           break;
       }
    }
    switch(event->key())
    {
        case Qt::Key_ParenLeft:
        {            
            cursor.insertText("()");
            cursor.movePosition(QTextCursor::PreviousCharacter);
            this->setTextCursor(cursor);
            return;
        }
        case Qt::Key_Return:
        {
            QString command;
            cursor.select(QTextCursor::BlockUnderCursor);
            command = cursor.selectedText();

            command.remove("---> ");
            command = command.trimmed();

            if(command.isEmpty())
            {
                showResult("error");
                return;
            }

            command.remove(QChar(';'));
            command += ';';

            emit sendCommand(command);
            return;
        }
        case Qt::Key_Up:
        {
            bool canUp;
            do{
                canUp = cursor.movePosition(QTextCursor::PreviousBlock);
            }while(!isLineCommand(cursor.block()) && canUp);
            if(canUp)
            {                
                cursor.movePosition(QTextCursor::EndOfBlock);
                this->setTextCursor(cursor);
            }
            return;
        }
        case Qt::Key_Down:
        {
            do{
                cursor.movePosition(QTextCursor::NextBlock);
            }while(!isLineCommand(cursor.block()));
            cursor.movePosition(QTextCursor::EndOfBlock);
            this->setTextCursor(cursor);
            return;
        }
        case Qt::Key_Home:
            QTextEdit::keyPressEvent(event);        
            cursor = this->textCursor();
            cursor.movePosition(QTextCursor::NextWord);
            this->setTextCursor(cursor);
            return;
        case Qt::Key_Backspace:
        case Qt::Key_Left:
        {
            if(cursor.block().position()+ 5 >= cursor.position())
            {
                return;
            }
            goto A;
        }
        case Qt::Key_Right:
        case Qt::Key_Delete:
        {
            if(cursor.block().position()+ 4 >= cursor.position())
            {
                return;
            }
            else
                if(cursor.atBlockEnd() || cursor.atEnd())
                    return;
            break;
        }
   A:     default:
        {
            bool isShortcut = ((event->modifiers() & Qt::ControlModifier) && event->key() == Qt::Key_Space);
            if (!c || !isShortcut) // dont process the shortcut when we have a completer
            {
                QTextEdit::keyPressEvent(event);
            }
            const bool ctrlOrShift = event->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
            if (!c || (ctrlOrShift && event->text().isEmpty()))
                return;

            static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
            bool hasModifier = (event->modifiers() != Qt::NoModifier) && !ctrlOrShift;
            QString completionPrefix = textUnderCursor();
            //if(completionPrefix.isEmpty()) return;

            if (!isShortcut && (hasModifier || event->text().isEmpty()|| completionPrefix.length() < 3
                      || eow.contains(event->text().right(1)))) {
                c->popup()->hide();
                return;
            }

            if (completionPrefix != c->completionPrefix()) {
                c->setCompletionPrefix(completionPrefix);
                c->popup()->setCurrentIndex(c->completionModel()->index(0, 0));
            }
            QRect cr = cursorRect();
            cr.setWidth(c->popup()->sizeHintForColumn(0)
                        + c->popup()->verticalScrollBar()->sizeHint().width());
            c->complete(cr); // popup it up!
            return;
        }
    }
    QTextEdit::keyPressEvent(event);
}
void CalTextEdit::mousePressEvent ( QMouseEvent * event )
{
    QTextCursor cursor(this->textCursor());
    if(event->button() == Qt::RightButton)// || cursor.hasSelection())
    {
        return;
    }
    QTextEdit::mousePressEvent(event);
    cursor = this->textCursor();
    if(isLineCommand(cursor.block()))
    {
        if(cursor.block().position() + 5 > cursor.position())
        {
            cursor.movePosition(QTextCursor::EndOfBlock);
            this->setTextCursor(cursor);            
        }
    }
}
void CalTextEdit::mouseReleaseEvent(QMouseEvent * event )
{
    QTextEdit::mouseReleaseEvent(event);
}
void CalTextEdit::mouseMoveEvent (QMouseEvent * event )
{
    QTextEdit::mouseMoveEvent(event);
}
 void CalTextEdit::contextMenuEvent(QContextMenuEvent *event)
 {
 }
void CalTextEdit::mouseDoubleClickEvent(QMouseEvent * event )
{
    QTextEdit::mouseDoubleClickEvent(event);
    QTextCursor cursor(this->textCursor());
    if(isLineCommand(cursor.block()))
    {
        if(cursor.block().position() + 5 > cursor.position())
        {
            cursor.movePosition(QTextCursor::EndOfBlock);
            this->setTextCursor(cursor);
        }
    }
}

void CalTextEdit::getResult(QString result)
{
    if(showResult(result))
        createNewCommand();
}
bool CalTextEdit::isLineCommand(QTextBlock textBlock)
{
    QString command = textBlock.text();
    if(command.indexOf("---> ") >= 0)
        return true;
    return false;
}
void CalTextEdit::receiveFunction(QString function)
{
    QTextCursor coursor = this->textCursor();
    if(coursor.hasSelection() || !isLineCommand(coursor.block()))
        return;
    coursor.insertText(function);
    coursor.movePosition(QTextCursor::PreviousCharacter);    
}
void CalTextEdit::moveCursorToEnd()
{
    QTextCursor curCutsor = this->textCursor();
    curCutsor.movePosition(QTextCursor::EndOfBlock);
    setTextCursor(curCutsor);
}
